package com.yycx.common.poi;

import cn.afterturn.easypoi.entity.ImageEntity;
import cn.afterturn.easypoi.word.parse.excel.ExcelMapParse;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.xmlbeans.XmlCursor;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * easyPoi 循环扩展
 * 功能：
 * 一，word表格模板循环
 * 1，表格
 */
public class EasyPoiPlus {
    public void doEasyPoiPlus(XWPFDocument document, Map<String, Object> param) {
        if (document == null) {
            System.out.println("【EasyPoiPlus】XWPFDocument对象为null");
            return;
        }
        if (param == null) {
            System.out.println("【EasyPoiPlus】替换参数对象为null");
            return;
        }
        List<IBodyElement> bodyElements = document.getBodyElements();// 所有对象（段落+表格）
        int templateBodySize = bodyElements.size();// 标记模板文件（段落+表格）总个数
        int curP = 0;// 当前操作段落对象的索引
        int startLoop = -1;
        int endLoop = -1;
        Object listData = null;//自定义循环参数
        out:
        for (int a = 0; a < templateBodySize; a++) {
            IBodyElement body = bodyElements.get(a);
            //循环标签只能写到段落上
            //注意  循环内容通过  [{xxx}]取数据   xxx为key    itemIndex   为循环角标
            /**例如
             *   [[fe:list   序号：[{itemIndex}]
             *      {{fe:[{xxx}]    t.name}}
             *   ]]
             *
             */
            if (BodyElementType.PARAGRAPH.equals(body.getElementType())) {
                XWPFParagraph paragraph = body.getBody().getParagraphs().get(curP);
                String paragraphText = paragraph.getText();
                String startRegEx = "\\[\\[fe:.+?";
                Pattern startPattern = Pattern.compile(startRegEx);
                Matcher startMatcher = startPattern.matcher(paragraphText);//正则匹配字符串[{fe:***

                String endRegEx = "\\]\\]";
                Pattern endPattern = Pattern.compile(endRegEx);
                Matcher endMatcher = endPattern.matcher(paragraphText);//正则匹配字符串[{fe:***

                if (startMatcher.find()) {//找到自定义循环头标签
                    //循环参数key为 [[fe:key
                    String key = paragraphText.substring(paragraphText.indexOf("[[fe:") + 5).split("\t")[0];
                    listData = param.get(key);
                    if (key == null || listData == null) {
                        System.out.println("自定义循环获取数据为空");
                        return;
                    }

                    startLoop = a;
                }

                if (endMatcher.find()) {//找到自定义循环尾标签
                    endLoop = a;
                }

                if (startLoop != -1 && endLoop != -1) {
                    break out;
                }
                curP++;
            }
        }
        int delEndLoopIndex = endLoop;
        if (startLoop != -1 && endLoop != -1) {//处理标签内容
            //1，复制标签中的所有table和paragraph的内容及样式
            //2，将复制的table和paragraph循环添加到startLoop位置
            //3，循环自定义标签变量，赋值
            //4，删除startLoop到endLoop的模板


            //
            List<IBodyElement> tempBodyElements = new ArrayList<IBodyElement>();
            int beginInsertIndex = endLoop + 1;
            //循环参数
            if (listData == null || !(listData instanceof List)) {
                System.out.println("自定义循环获取数据为空或不是List对象");
                return;
            }
            List<Map<String, Object>> lists = (List<Map<String, Object>>) listData;
            //结束标签前循环插入模板

            int insertCursorIndex = endLoop;
            IBodyElement endLoopElement = bodyElements.get(endLoop);
            XWPFParagraph endParagraph = (XWPFParagraph) endLoopElement;
            //在自定义循环末尾标签前面循环插入循环内容
            //最后应该删除循环末尾标签


            for (int n = 0; n < lists.size(); n++) {
                Map<String, Object> item = lists.get(n);
                item.put("itemIndex", n);
                for (int i = startLoop + 1; i < endLoop; i++) {
                    XmlCursor insertCursor = endParagraph.getCTP().newCursor();
                    IBodyElement body = bodyElements.get(i);
                    if (BodyElementType.PARAGRAPH.equals(body.getElementType())) {
                        XWPFParagraph paragraph = (XWPFParagraph) body;
                        XWPFParagraph insertNewParagraph = document.insertNewParagraph(insertCursor);
                        delEndLoopIndex++;
                        //将模板段落内容及样式复制到新添加的段落
                        this.copyParagraph(document, insertNewParagraph, paragraph);
                        //解析新添加段落的自定义标签
                        replaceParagraph(insertNewParagraph, item, param);
                        //XWPFTable newCreateTable = document.createTable();
                    } else if (BodyElementType.TABLE.equals(body.getElementType())) {
                        XWPFTable table = (XWPFTable) body;
                        XWPFTable insertNewTable = document.insertNewTbl(insertCursor);
                        delEndLoopIndex++;
                        List<XWPFTableRow> templateTableRows = table.getRows();// 获取模板表格所有行
                        for (int t = 0; t < templateTableRows.size(); t++) {
                            XWPFTableRow newCreateRow = insertNewTable.createRow();
                            CopyTableRow(newCreateRow, templateTableRows.get(t));// 复制模板行文本和样式到新行
                        }
                        insertNewTable.removeRow(0);// 移除多出来的第一行
                        replaceTable(insertNewTable, item, param);//替换标签
                    }
                }
             /*   XmlCursor insertCursor = endParagraph.getCTP().newCursor();
                XWPFParagraph insertNewParagraph = document.insertNewParagraph(insertCursor);
                insertNewParagraph.setPageBreak(true);
                delEndLoopIndex++;*/
            }

            //循环完成后删除循环结束标签   和自定义循环模板
            document.removeBodyElement(delEndLoopIndex);
            for (int i = endLoop - 1; i >= startLoop; i--) {
                document.removeBodyElement(i);
            }


        }
    }


    /**
     * 复制文本段落XWPFParagraph格式
     */
    private void copyParagraph(XWPFDocument document, XWPFParagraph newParagraph, XWPFParagraph templateParagraph) {
        // 设置段落样式
        newParagraph.getCTP().setPPr(templateParagraph.getCTP().getPPr());
        // 添加Run标签
        for (int pos = 0; pos < newParagraph.getRuns().size(); pos++) {
            newParagraph.removeRun(pos);

        }
        for (XWPFRun s : templateParagraph.getRuns()) {
            XWPFRun targetrun = newParagraph.createRun();
            CopyRun(document, targetrun, s);
        }
    }

    /**
     * 复制文本节点run
     */
    public void CopyRun(XWPFDocument document, XWPFRun newRun, XWPFRun templateRun) {
        newRun.getCTR().setRPr(templateRun.getCTR().getRPr());
        // 设置文本
        newRun.setText(templateRun.text());
    }


    /**
     * 复制表格行XWPFTableRow格式
     */
    private void CopyTableRow(XWPFTableRow target, XWPFTableRow source) {

        int tempRowCellsize = source.getTableCells().size();// 模板行的列数
        for (int i = 0; i < tempRowCellsize - 1; i++) {
            target.addNewTableCell();// 为新添加的行添加与模板表格对应行行相同个数的单元格
        }
        // 复制样式
        target.getCtRow().setTrPr(source.getCtRow().getTrPr());
        // 复制单元格
        for (int i = 0; i < target.getTableCells().size(); i++) {
            copyTableCell(target.getCell(i), source.getCell(i));
        }
    }

    /**
     * 复制单元格XWPFTableCell格式
     */
    private void copyTableCell(XWPFTableCell newTableCell, XWPFTableCell templateTableCell) {
        // 列属性
        newTableCell.getCTTc().setTcPr(templateTableCell.getCTTc().getTcPr());
        // 删除目标 targetCell 所有文本段落
        for (int pos = 0; pos < newTableCell.getParagraphs().size(); pos++) {
            newTableCell.removeParagraph(pos);
        }
        // 添加新文本段落
        for (XWPFParagraph sp : templateTableCell.getParagraphs()) {
            XWPFParagraph targetP = newTableCell.addParagraph();
            copyParagraph(targetP, sp);
        }
    }

    /**
     * 复制文本段落XWPFParagraph格式
     */
    private void copyParagraph(XWPFParagraph newParagraph, XWPFParagraph templateParagraph) {
        // 设置段落样式
        newParagraph.getCTP().setPPr(templateParagraph.getCTP().getPPr());
        // 添加Run标签
        for (int pos = 0; pos < newParagraph.getRuns().size(); pos++) {
            newParagraph.removeRun(pos);

        }
        for (XWPFRun s : templateParagraph.getRuns()) {
            XWPFRun targetrun = newParagraph.createRun();
            CopyRun(targetrun, s);
        }

    }

    /**
     * 复制文本节点run
     */
    private void CopyRun(XWPFRun newRun, XWPFRun templateRun) {
        newRun.getCTR().setRPr(templateRun.getCTR().getRPr());
        // 设置文本
        newRun.setText(templateRun.text());
    }


    /**
     * 根据map替换表格中的[{key}]标签
     */
    public void replaceTable(XWPFTable xwpfTable, Map<String, Object> parametersMap, Map<String, Object> outParam) {
        List<XWPFTableRow> rows = xwpfTable.getRows();
        for (XWPFTableRow xWPFTableRow : rows) {
            List<XWPFTableCell> tableCells = xWPFTableRow.getTableCells();
            for (XWPFTableCell xWPFTableCell : tableCells) {
                List<XWPFParagraph> paragraphs2 = xWPFTableCell.getParagraphs();
                for (XWPFParagraph xWPFParagraph : paragraphs2) {
                    replaceParagraph(xWPFParagraph, parametersMap, outParam);
                }
            }
        }
    }


    /**
     * 根据map替换段落元素内的[{}]标签，如果值为list   则替换为UUID生成的key，同时将值set进map  key为也为UUID
     */
    public void replaceParagraph(XWPFParagraph xWPFParagraph, Map<String, Object> parametersMap, Map<String, Object> outParam) {
        List<XWPFRun> runs = xWPFParagraph.getRuns();
        String xWPFParagraphText = xWPFParagraph.getText();
        String regEx = "\\[\\{.+?\\}\\]";
        Pattern pattern = Pattern.compile(regEx);
        Matcher matcher = pattern.matcher(xWPFParagraphText);//正则匹配字符串[{****}]

        if (matcher.find()) {
            // 查找到有标签才执行替换
            int beginRunIndex = xWPFParagraph.searchText("[{", new PositionInParagraph()).getBeginRun();// 标签开始run位置
            int endRunIndex = xWPFParagraph.searchText("}]", new PositionInParagraph()).getEndRun();// 结束标签
            StringBuffer key = new StringBuffer();

            if (beginRunIndex == endRunIndex) {
                // [{**}]在一个run标签内
                XWPFRun beginRun = runs.get(beginRunIndex);
                String beginRunText = beginRun.text();

                int beginIndex = beginRunText.indexOf("[{");
                int endIndex = beginRunText.indexOf("}]");
                int length = beginRunText.length();

                if (beginIndex == 0 && endIndex == length - 2) {
                    // 该run标签只有[{**}]
                    XWPFRun insertNewRun = xWPFParagraph.insertNewRun(beginRunIndex);
                    insertNewRun.getCTR().setRPr(beginRun.getCTR().getRPr());
                    // 设置文本
                    key.append(beginRunText.substring(2, endIndex));
                    //判断value类型为String还是List
                    Object val = getValueBykey(key.toString().trim(), parametersMap);
                    if (val instanceof String) {
                        insertNewRun.setText(val == null ? "" : val.toString());
                    } else if (val instanceof List) {
                        String listKey = "k" + UUID.randomUUID().toString().replace("-", "");
                        insertNewRun.setText(listKey);
                        outParam.put(listKey, val);
                    } else if (val instanceof ImageEntity) {
                        insertNewRun.setText("", 0);
                        ExcelMapParse.addAnImage((ImageEntity) val, insertNewRun);
                    } else {
                        insertNewRun.setText(val == null ? "" : val.toString());
                    }
                    xWPFParagraph.removeRun(beginRunIndex + 1);
                } else {
                    // 该run标签为**[{**}]** 或者 **[{**}] 或者[{**}]**，替换key后，还需要加上原始key前后的文本
                    XWPFRun insertNewRun = xWPFParagraph.insertNewRun(beginRunIndex);
                    insertNewRun.getCTR().setRPr(beginRun.getCTR().getRPr());
                    key.append(beginRunText.substring(beginRunText.indexOf("[{") + 2, beginRunText.indexOf("}]")));
                    // 设置文本
                    Object val = getValueBykey(key.toString(), parametersMap);
                    Object setVal = "";
                    if (val instanceof String) {
                        setVal = val == null ? "" : val.toString();
                    } else if (val instanceof List) {
                        String listKey = "k" + UUID.randomUUID().toString().replace("-", "");
                        outParam.put(listKey, val);
                        setVal = listKey;
                    } else if (val instanceof ImageEntity) {
                        insertNewRun.setText("", 0);
                        ExcelMapParse.addAnImage((ImageEntity) val, insertNewRun);
                    } else {
                        setVal = val == null ? "" : val.toString();
                    }
                    String textString = beginRunText.substring(0, beginIndex) + setVal
                            + beginRunText.substring(endIndex + 2);
                    insertNewRun.setText(textString);
                    xWPFParagraph.removeRun(beginRunIndex + 1);
                }

            } else {
                // [{**}]被分成多个run

                //先处理起始run标签,取得第一个[{key}]值
                XWPFRun beginRun = runs.get(beginRunIndex);
                String beginRunText = beginRun.text();
                int beginIndex = beginRunText.indexOf("[{");
                if (beginRunText.length() > 1) {
                    key.append(beginRunText.substring(beginIndex + 2));
                }
                ArrayList<Integer> removeRunList = new ArrayList<>();//需要移除的run
                //处理中间的run
                for (int i = beginRunIndex + 1; i < endRunIndex; i++) {
                    XWPFRun run = runs.get(i);
                    String runText = run.text();
                    key.append(runText);
                    removeRunList.add(i);
                }

                // 获取endRun中的key值
                XWPFRun endRun = runs.get(endRunIndex);
                String endRunText = endRun.text();
                int endIndex = endRunText.indexOf("}]");
                //run中**}]或者**}]**
                if (endRunText.length() > 1 && endIndex != 0) {
                    key.append(endRunText.substring(0, endIndex));
                }


                //*******************************************************************
                //取得key值后替换标签

                //先处理开始标签
                if (beginRunText.length() == 2) {
                    // run标签内文本[{
                    XWPFRun insertNewRun = xWPFParagraph.insertNewRun(beginRunIndex);
                    insertNewRun.getCTR().setRPr(beginRun.getCTR().getRPr());
                    // 设置文本
                    Object val = getValueBykey(key.toString(), parametersMap);
                    if (val instanceof String) {
                        insertNewRun.setText(val == null ? "" : val.toString());
                    } else if (val instanceof List) {
                        String listKey = "k" + UUID.randomUUID().toString().replace("-", "");
                        insertNewRun.setText(listKey);
                        outParam.put(listKey, val);
                    } else if (val instanceof ImageEntity) {
                        insertNewRun.setText("", 0);
                        ExcelMapParse.addAnImage((ImageEntity) val, insertNewRun);
                    } else {
                        insertNewRun.setText(val == null ? "" : val.toString());
                    }
                    xWPFParagraph.removeRun(beginRunIndex + 1);//移除原始的run
                } else {
                    // 该run标签为**[{**或者 [{** ，替换key后，还需要加上原始key前的文本
                    XWPFRun insertNewRun = xWPFParagraph.insertNewRun(beginRunIndex);
                    insertNewRun.getCTR().setRPr(beginRun.getCTR().getRPr());
                    // 设置文本
                    Object val = getValueBykey(key.toString(), parametersMap);
                    Object setVal = "";
                    if (val instanceof String) {
                        setVal = val == null ? "" : val.toString();
                    } else if (val instanceof List) {
                        String listKey = "k" + UUID.randomUUID().toString().replace("-", "");
                        outParam.put(listKey, val);
                        setVal = listKey;
                    } else if (val instanceof ImageEntity) {
                        insertNewRun.setText("", 0);
                        ExcelMapParse.addAnImage((ImageEntity) val, insertNewRun);
                    } else {
                        setVal = val == null ? "" : val.toString();
                    }
                    String textString = beginRunText.substring(0, beginRunText.indexOf("[{")) + setVal;
                    insertNewRun.setText(textString);
                    xWPFParagraph.removeRun(beginRunIndex + 1);//移除原始的run
                }

                //处理结束标签
                if (endRunText.length() == 2) {
                    // run标签内文本只有}]
                    XWPFRun insertNewRun = xWPFParagraph.insertNewRun(endRunIndex);
                    insertNewRun.getCTR().setRPr(endRun.getCTR().getRPr());
                    // 设置文本
                    insertNewRun.setText("");
                    xWPFParagraph.removeRun(endRunIndex + 1);//移除原始的run

                } else {
                    // 该run标签为**}]**或者 }]** 或者**}]，替换key后，还需要加上原始key后的文本
                    XWPFRun insertNewRun = xWPFParagraph.insertNewRun(endRunIndex);
                    insertNewRun.getCTR().setRPr(endRun.getCTR().getRPr());
                    // 设置文本
                    String textString = endRunText.substring(endRunText.indexOf("}]") + 2);
                    insertNewRun.setText(textString);
                    xWPFParagraph.removeRun(endRunIndex + 1);//移除原始的run
                }

                //处理中间的run标签
                for (int i = 0; i < removeRunList.size(); i++) {
                    XWPFRun xWPFRun = runs.get(removeRunList.get(i));//原始run
                    XWPFRun insertNewRun = xWPFParagraph.insertNewRun(removeRunList.get(i));
                    insertNewRun.getCTR().setRPr(xWPFRun.getCTR().getRPr());
                    insertNewRun.setText("");
                    xWPFParagraph.removeRun(removeRunList.get(i) + 1);//移除原始的run
                }

            }

            replaceParagraph(xWPFParagraph, parametersMap, outParam);

        }
    }

    private Object getValueBykey(String key, Map<String, Object> map) {
        Object returnValue = "";
        if (key != null) {
            try {
                returnValue = map.get(key) != null ? map.get(key) : "";
            } catch (Exception e) {
                // TODO: handle exception
                System.out.println("key:" + key + "***" + e);
                returnValue = "";
            }

        }
        return returnValue;
    }
}